package com.ctshk.rpc.payment.strategy.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.ctshk.common.enums.SystemError;
import com.ctshk.common.exception.BusinessException;
import com.ctshk.common.utils.Assert;
import com.ctshk.common.utils.HttpUtils;
import com.ctshk.common.utils.MD5Util;
import com.ctshk.rpc.payment.dto.PayDetailsDTO;
import com.ctshk.rpc.payment.dto.RefundDetailsDTO;
import com.ctshk.rpc.payment.dto.WeChatPayConfig;
import com.ctshk.rpc.payment.dto.rep.wechat.QueryOrderInfoDTO;
import com.ctshk.rpc.payment.dto.rep.wechat.WeChatPayCallBackDTO;
import com.ctshk.rpc.payment.dto.rep.wechat.WxPayRefundDetailDTO;
import com.ctshk.rpc.payment.dto.req.PayRefundReq;
import com.ctshk.rpc.payment.dto.req.wechat.*;
import com.ctshk.rpc.payment.entity.PayRecord;
import com.ctshk.rpc.payment.enums.PayOrderStatus;
import com.ctshk.rpc.payment.enums.PaymentMethod;
import com.ctshk.rpc.payment.factory.PayDetailsDTOSimpleFactory;
import com.ctshk.rpc.payment.factory.RefundDetailsDTOSimpleFactory;
import com.ctshk.rpc.payment.req.PayCommonReq;
import com.ctshk.rpc.payment.strategy.PayStrategy;
import com.ctshk.rpc.payment.thirdparty.weixin.WxPayResultEntity;
import com.ctshk.rpc.payment.utils.wechat.WeChatPayUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;

/**
 * @BelongsProject: ctshk-cloud
 * @BelongsPackage: com.ctshk.rpc.payment.strategy.impl
 * @Author: hef
 * @CreateTime: 2021-04-26 11:38
 * @Description: 微信支付
 */
@Slf4j
@Component
public class WeChatPayStrategy extends PayStrategy<WeChatPayConfig> {

    @Value("${spring.profiles.active}")
    private String active;

    private static final String PRE_URL = "https://pay.hkfocusvision.com/payment";
    public WeChatPayStrategy() {
        super(WeChatPayConfig.class, PaymentMethod.WE_CHAT_PAY_APP);
    }

    @Override
    public Object pay(PayCommonReq req) throws Exception {
        // 微信金额单位是分，默认乘100
        BigDecimal totalFee = req.getTotalFee().multiply(new BigDecimal(100));
        req.setTotalFee(totalFee);
        WeChatPayConfig config = getConfig();
        WeChatPayTransactionReq transactionReq = new WeChatPayTransactionReq(getMethod(req.getPaymentMethod()),
                config, req.getTotalFee(), req.getBody(), String.valueOf(req.getOrderId()));
        //App支付还需要AppId
        String reqStr = null;
        if (PaymentMethod.WE_CHAT_PAY_APP.equals(req.getPaymentMethod())) { // APP
            long time = System.currentTimeMillis() / 1000;
            String str = config.getMId() + config.getMKey() + time;
            String sign = MD5Util.stringMd5(str).toUpperCase(Locale.ROOT);

            JSONObject jsonObject = new JSONObject();
            jsonObject.put("method", "inapp");
            jsonObject.put("out_trade_no", String.valueOf(req.getOrderId()));
            jsonObject.put("sub_appid", config.getAppId());
            jsonObject.put("body", req.getSubject());
            jsonObject.put("detail", req.getBody());
            jsonObject.put("notify_url", config.getNotifyUrl());
            jsonObject.put("mid", config.getMId());
            jsonObject.put("stime", time);
            jsonObject.put("sign", sign);
            jsonObject.put("total_fee", transactionReq.getTotalFee());
            reqStr = jsonObject.toJSONString();
        } else if (PaymentMethod.WE_CHAT_PAY_H5.equals(req.getPaymentMethod())) { // 唤起微信支付 - 微信香港本地支付
            long time = System.currentTimeMillis() / 1000;
            String str = config.getMId() + config.getMKey() + time;
            String sign = MD5Util.stringMd5(str).toUpperCase(Locale.ROOT);

            String wechatOrderUrl = PRE_URL + "/public/index.php?s=api/pay";
            HashMap<String, String> paramMap = new HashMap<>();
            paramMap.put("s", "api/pay");
            paramMap.put("method", "getOauthCode");
            paramMap.put("mid", config.getMId());
            paramMap.put("stime", String.valueOf(time));
            paramMap.put("sign", sign);
            paramMap.put("out_trade_no", String.valueOf(req.getOrderId()));
            paramMap.put("sub_appid", config.getAppId());
            paramMap.put("body", req.getSubject());
            paramMap.put("detail", req.getBody());
            paramMap.put("notify_url", config.getNotifyUrl());
            log.info("【支付服务-微信支付】H5支付请求, reqUrl:{}, param:{}", config.getReqUrl(), JSON.toJSONString(paramMap));
            String responseStr = HttpUtils.sendHttpSslGet(wechatOrderUrl, paramMap);
            log.info("【支付服务-微信支付】H5支付响应, data:{}", responseStr);
            config.setReqUrl(PRE_URL + "/public/index.php?s=api/pay/directHk");
        } else { // native(微信native就是给二维码)
            reqStr = transactionReq.toJsonString();
        }
        log.info("【支付服务-微信支付】第三方请求, url:{}, data:{}", config.getReqUrl(), JSON.toJSONString(transactionReq));
        WxPayResultEntity resultEntity = sendPost(config.getReqUrl(), reqStr);
        log.info("【支付服务-微信支付】第三方响应, data:{}", JSON.toJSONString(resultEntity));
        if (!resultEntity.isSuccess()) {
            throw new BusinessException(SystemError.PAY_22001);
        }
        JSONObject data = JSON.parseObject(resultEntity.getData().toString());
        if (PaymentMethod.WE_CHAT_PAY_APP.equals(req.getPaymentMethod())) {
            return data.get("info");
        } else {
            return data.get("code_url").toString();
        }
    }

    @Override
    public boolean payCallback(Map<String, String> paramMap) {
        WeChatPayConfig config = getConfig();
        Assert.isTrue(WeChatPayUtil.isSignatureValid(config, paramMap), SystemError.PAY_22005);
        String callbackJson = JSON.toJSONString(paramMap);
        WeChatPayCallBackDTO dto = JSON.parseObject(callbackJson, WeChatPayCallBackDTO.class);
        //获取支付记录
        PayRecord payRecord = payRecordMapper.selectByOrderId(Long.valueOf(dto.getOSn()));
        Assert.nonNull(payRecord, SystemError.PAY_22004);
        //校验金额
        BigDecimal amt = BigDecimal.valueOf(Long.valueOf(dto.getTotalFee())).divide(BigDecimal.valueOf(100));

//        if ("local".equals(active) || "test".equals(active)) {
//            Assert.isTrue(new BigDecimal("0.1").compareTo(amt) == 0, SystemError.PAY_22015);
//        } else {
//            Assert.isTrue(payRecord.getTotalFee().compareTo(amt) == 0, SystemError.PAY_22015);
//        }
        Assert.isTrue(payRecord.getTotalFee().compareTo(amt) == 0, SystemError.PAY_22015);
        //支付状态
        PayOrderStatus status = dto.getIsPay().equals("1") ? PayOrderStatus.FULFIL : PayOrderStatus.PAYMENT_ERROR;
        //修改支付记录
        PayRecord update = new PayRecord();
        update.setId(payRecord.getId());
        update.setOrderStatus(status.getStatus());
        update.setGmtModified(LocalDateTime.now());
        update.setCallbackParam(callbackJson);
        update.setPayTime(LocalDateTime.now());
        int i = payRecordMapper.updateById(update);
        return i > 0;
    }

    @Override
    public PayDetailsDTO getPayDetails(String orderNumber) {
        WeChatPayConfig config = getConfig();
        WeChatPayQueryTransactionReq req =
                new WeChatPayQueryTransactionReq(
                        "query", config, orderNumber);
        WxPayResultEntity resultEntity = sendPost(config.getReqUrl(), req.toJsonString());
        if (resultEntity.isSuccess()) {
            QueryOrderInfoDTO infoDTO = JSON.parseObject(resultEntity.getDataJsonString(), QueryOrderInfoDTO.class);
            return PayDetailsDTOSimpleFactory.produce(infoDTO);
        }
        return null;
    }

    @Override
    public RefundDetailsDTO getRefundDetail(String orderNumber, String refundNumber) {
        WeChatPayConfig config = getConfig();
        WeChatPayRefundQueryReq refundQueryReq = new WeChatPayRefundQueryReq("queryRefund", config, orderNumber);
        WxPayResultEntity resultEntity = sendPost(config.getReqUrl(), refundQueryReq.toJsonString());
        Assert.isTrue(resultEntity.isSuccess(), SystemError.PAY_22009);
        System.err.println(resultEntity.getData().toString());
        WxPayRefundDetailDTO refundDetailDTO = JSON.parseObject(resultEntity.getDataJsonString(), WxPayRefundDetailDTO.class);
        RefundDetailsDTO produce = RefundDetailsDTOSimpleFactory.produce(refundDetailDTO);
        return Objects.nonNull(produce) ? produce.setOrderNumber(orderNumber) : null;
    }

    @Override
    public Object refundNotify(Map<String, String> paramMap) {
        // TODO HK微信支付没有退款回调
        return null;
    }

    @Override
    public boolean refund(PayRefundReq req) {
        WeChatPayConfig config = getConfig();
        WeChatPayRefundReq refundReq = new WeChatPayRefundReq("refund", config, req.getOrderNumber(), req.getRefundAmount(), req.getRefundNumber());
        WxPayResultEntity resultEntity = sendPost(config.getReqUrl(), refundReq.toJsonString());
        Assert.isTrue(resultEntity.isSuccess(), SystemError.PAY_22010, resultEntity.getMsg());
        return true;
    }

    @Override
    protected boolean checkPayDetails(Map<String, String> paramMap) {
        WeChatPayCallBackDTO callback = JSON.parseObject(JSON.toJSONString(paramMap), WeChatPayCallBackDTO.class);
        PayDetailsDTO dto = getPayDetails(callback.getOSn());
        //校验金额
        String totalFee = callback.getTotalFee();
        long l = Long.valueOf(totalFee);
        Assert.isTrue(dto.getOrderAmount().equals(BigDecimal.valueOf(l).divide(BigDecimal.valueOf(100))) &&
                dto.getPrimaryStatus().equals(callback.getIsPay()), SystemError.PAY_22017);
        return true;
    }

    /**
     * 获取支付方法
     *
     * @param method
     * @return
     */
    private String getMethod(PaymentMethod method) {
        switch (method) {
            case WE_CHAT_PAY_NATIVE:
                return "native";
            case WE_CHAT_PAY_APP:
                return "inapp";
            default:
                throw new BusinessException(SystemError.PAY_22000);
        }
    }

    /**
     * 发送post请求
     *
     * @param targetUrl
     * @param req
     * @return
     */
    private WxPayResultEntity sendPost(String targetUrl, String req) {
        String result = null;
        try {
            result = HttpUtils.sendHttpSslPost(targetUrl, req);
        } catch (Exception e) {
            throw new BusinessException(SystemError.PAY_22008);
        }
        WxPayResultEntity wxPayResultEntity = JSON.parseObject(result, WxPayResultEntity.class);
        Assert.nonNull(wxPayResultEntity, SystemError.PAY_22008);
        return wxPayResultEntity;
    }
}
